daslang: -log-compile-time CLI flag for per-module compile diagnostics#2680
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new daslang CLI flag (-log-compile-time) and corresponding CodeOfPolicies::log_module_compile_time option to emit per-module compile diagnostics (phase breakdown + simulate timing), including surfacing the macro-driven type-inference pass count.
Changes:
- Introduces
log_module_compile_timeas aCodeOfPoliciesoption and wires it throughdaslangCLI parsing and policy construction. - Adds per-module timing breakdown logging in parsing/compilation and expands simulate-time logging to include module identity.
- Updates RTTI registration and documentation so the new policy/option is visible and described (plus a fix to
CodeOfPolicieshandmade docs alignment).
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| utils/daScript/main.cpp | Adds -log-compile-time flag, help text, and maps it into CodeOfPolicies. |
| include/daScript/ast/ast.h | Adds CodeOfPolicies::log_module_compile_time and Program::inferPassesUsed. |
| src/ast/ast_infer_type.cpp | Records inferPassesUsed after the outer macro-driven inference loop. |
| src/ast/ast_parse.cpp | Collects per-module phase timings and emits per-module compile breakdown when enabled; extends top-level summary gating. |
| src/ast/ast_simulate.cpp | Enables simulate timing logs for the new option and includes module name + file in output. |
| src/builtin/module_builtin_rtti.cpp | Registers log_module_compile_time for RTTI/for_each_field visibility. |
| doc/source/reference/language/options.rst | Documents the new log_module_compile_time option. |
| doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst | Adds missing max_call_depth description line and documents the new policy field. |
| mouse-data/docs/*.md | Adds new “mouse cards” documenting related gotchas and workflows. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
13b1f6a to
499e906
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (2)
src/ast/ast_infer_type.cpp:5752
inferPassesUsedis described as an “outer macro-driven pass count”, but it’s currently accumulated from the innerinferTypesDirtyloop variable (pass) and uses the 0-based index value at loop exit. This can undercount (e.g. a single successful infer pass can record 0) and may not match the intended “# of infer cycles due to macros” reported byProgram::inferTypes’ ownpassloop. Consider either (1) recording the outerProgram::inferTypespass count explicitly, or (2) renaming/commenting the field and adjusting the math to reflect the actual number of infer iterations executed.
This issue also appears on line 5884 of the same file.
void Program::inferTypes(TextWriter &logs, ModuleGroup &libGroup) {
newLambdaIndex = 1;
inferPassesUsed = 0; // accumulated by inferTypesDirty across all calls
inferTypesDirty(logs, false);
bool anyMacrosDidWork = false;
bool anyMacrosFailedToInfer = false;
int pass = 0;
int32_t maxInferPasses = options.getIntOption("max_infer_passes", policies.max_infer_passes);
src/ast/ast_infer_type.cpp:5891
inferPassesUsed += passusespassas a 0-based iteration index when the loop breaks viacontext.finished(), which can record 0 even though one inference iteration ran. If this value is meant to be user-facing in the compile-time logs, it should reflect the actual number of passes executed (usuallypass + 1for thecontext.finished()break path), or be computed from a dedicated counter.
if (pass == maxInferPasses) {
error("type inference exceeded maximum allowed number of passes (" + to_string(maxInferPasses) + ")\n"
"this is likely due to a loop in the type system",
"", "",
LineInfo(), CompilationError::exceeds_infer_passes);
}
inferPassesUsed += pass; // accumulate inner-loop passes across all inferTypesDirty calls in this module
}
499e906 to
a0cbd71
Compare
a0cbd71 to
769f432
Compare
769f432 to
83ae244
Compare
Adds new CLI flag and CodeOfPolicies::log_module_compile_time field that emits a per-module compile-time breakdown (parse / infer + pass count / optimize / macro / macro-mods) for every required module, plus a simulate-time line with the owning module name. Useful for diagnosing which submodule dominates compile time on large module graphs (motivating case: profiling a daspkg-installed dasImgui). - include/daScript/ast/ast.h: new policy field + Program::inferPassesUsed - src/ast/ast_infer_type.cpp: record outer macro pass count after inferTypes loop - src/ast/ast_parse.cpp: per-phase local accumulators in parseDaScript; new per-module breakdown log; extend top-level summary gate - src/ast/ast_simulate.cpp: simulate hook now logs module name and fires on either log_total_compile_time or log_module_compile_time - src/builtin/module_builtin_rtti.cpp: register log_module_compile_time for RTTI so daslib/rst.das + das2rst see the field - utils/daScript/main.cpp: -log-compile-time argv + policies wiring (both getPolicies() and compile_and_run paths) + help text - doc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst: add description for the new field; also fix a pre-existing missing description for max_call_depth that was silently shifting ~30 later field descriptions up by one in generated/rtti.rst - doc/source/reference/language/options.rst: row in options table - mouse-data/docs/: 3 new cards for the discoveries during this work (CodeOfPolicies-field full checklist, das2rst handmade positional mapping + silent-shift trap, daslang CLI flag wiring), plus a carryover JSON-cast card from a prior session Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
83ae244 to
ca0c985
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new
-log-compile-timeCLI flag on thedaslangexecutable, mapped to a newCodeOfPolicies::log_module_compile_timefield. When set, every required module emits its own parse / infer (with pass count) / optimize / macro (in infer) / macro mods breakdown plus its function count, everyProgram::simulateinvocation (macro modules during compile + the final entry-point simulate) logs its elapsed time with the owning module name, and the top-level aggregate summary fires regardless.Motivating use case:
daspkg install dasimguiglobally, then run arequire dasimguidriver under-log-compile-timeto see which submodules dominate compile time.The existing
log_compile_time(per-file one-liner) option is unchanged.log_total_compile_timeshares the new format (label + function count + clarifiedmacro (in infer)wording); confirmed with the repo owner this is not consumed by any tooling.Changed pieces
include/daScript/ast/ast.h): new/*option*/ bool log_module_compile_timenext to the existing log-time options; newint Program::inferPassesUsedto surface the sum ofinferTypesDirtyinner-loop passes.src/ast/ast_infer_type.cpp):Program::inferTypesresetsinferPassesUsed = 0;Program::inferTypesDirtyadds its localpassvalue at the end of each invocation. Result is the total number of times the inner type-inference loop ran for this module, across all macro-driven re-infer cycles.src/ast/ast_parse.cpp): local accumulatorsmyParseT/myInferT/myOptT/myMacroModTcapture per-phase deltas alongside the existing accumulation into*totParse/ etc.; the per-file log block at the end ofparseDaScriptemits the breakdown when the new policy is on. Gated onisDep— the entry script's per-module log is suppressed because its post-parseDaScriptwork (markExecutableSymbolUse/removeUnusedSymbols/deriveAliases/allocateStack/validateAst) runs incompileDaScript, so the top-level summary is authoritative for it.src/ast/ast_parse.cpp): now readstotal compile took X, fileName -- N functionsfollowed byrequire / parse / infer / optimize / macro (in infer) / macro modslines. Fires on eitherlog_total_compile_timeorlog_module_compile_time.src/ast/ast_simulate.cpp): existing simulate-time line now also fires on the new policy. WhenthisModule->nameis non-empty, prints, modName (fileName); when empty (the entry script), prints, fileNameplain. No noisy double-space.src/builtin/module_builtin_rtti.cpp): registerslog_module_compile_timesofor_each_field(used bydaslib/rst.dasanddas2rst) sees it.utils/daScript/main.cpp): file-scope staticlogModuleCompileTime,-log-compile-timeargv branch, policies wiring at BOTHgetPolicies()(AOT path) andcompile_and_run(run path), help-text line.doc/source/reference/language/options.rstoptions table; description line indoc/source/stdlib/handmade/structure_annotation-rtti-CodeOfPolicies.rst.CodeOfPoliciesdescription file was already missing a description formax_call_depth, which silently shifted ~30 later field descriptions up by one in the generatedrtti.rst. Added the missing line in the same commit since I touched the file anyway.Sample output (current format)
Running
bin/daslang -log-compile-time -dry-run examples/imgui_demo/main.das(full ImGui demo, ~80 required modules):Top consumers (sorted by wall-clock; one block per required module):
And on a no-module entry script (
examples/hello_world.das):(No
, (...)noise when module name is empty — handled at both the simulate line and the per-module log via theisDepgate.)Test plan
cmake --build build --config Release -j 64 --target daslangcleanexamples/hello_world.das— empty-modname simulate line prints, fileNameplain, no noisy commarequire strings_boost / linq / etc.) — every required module emits its own breakdown with function countimgui_demo/main.das— full real-world graph; 80 module breakdowns, top consumer iswidgets.dasat 0.96s wall / 0.86s infer / 57 passes / 1924 functionsoptions log_module_compile_time = trueenables breakdown for that fileinferTypesDirtypasses across all macro-driven re-infer cycles; macro-light modules show 11–12 passes, macro-heavy widget modules show 39–57dastest -- --test tests/— 8165/8171 passed, 0 failed, 0 errors, 6 skipped (matches master)test_aot -use-aot dastest/dastest.das -- --use-aot --failures-only --test tests— 7554/7560 passed, 0 failed, 0 errors, 6 skippedbin/daslang doc/reflections/das2rst.das— regen clean;log_module_compile_timeappears in generatedrtti.rstwith correct description; all surrounding fields realigned after themax_call_depthfixbuild succeeded.with no new warnings🤖 Generated with Claude Code